#include "MwadjProcessor.h"
#include <fstream>
#include <boost\graph\breadth_first_search.hpp>
#include <boost\lexical_cast.hpp>
#include <math.h>
#include <curlWrapper.h>

using namespace std;
using namespace boost;

MwadjProcessor::MwadjProcessor(void)
{
	mySystem firstSysnode;
	firstSysnode.acceleration = 0.0;
	firstSysnode.acc_chi_val =0;
	firstSysnode.chi_val = 0;
	firstSysnode.firing_rate =0.0;
	firstSysnode.freq =0;
	firstSysnode.sbp=0.0;
	firstSysnode.tick_time = 0;
	this->system_properties.insert(pair<double,mySystem>(0.0,firstSysnode));
}


MwadjProcessor::~MwadjProcessor(void)
{
}

void MwadjProcessor :: processinput(string filename)
{
	std::ifstream ipfilepointer;
	ipfilepointer.open("D:\\RA\\test.mwadj",ifstream::in);
	string linetoread;
	int global_vertex_id = 0;
	int global_edge_id = 0; 
	while(getline(ipfilepointer,linetoread))
	{
		vector<string> adj_edges;
		boost::split(adj_edges,linetoread,boost::is_any_of(":"));

		int vertex_u = atoi(adj_edges[0].c_str());

		int newer_vertex_u,newer_vertex_v;

		//***********************************************************************************************
		//the vertex id mapping is used to make sure that the vertex ids are all sequential. 
		//***********************************************************************************************
		map<int,int>::iterator to_find_new_id_u = this->vertex_id_mapping.find(vertex_u);		
		if ( to_find_new_id_u == this->vertex_id_mapping.end())
		{
			vertex_id_mapping.insert(pair<int,int>(vertex_u,global_vertex_id));
			newer_vertex_u = global_vertex_id;
			global_vertex_id++;
		}
		else
		{
			newer_vertex_u = to_find_new_id_u->second;
		}

		boost::trim_right_if(adj_edges[1],boost ::is_any_of(";"));
		boost::trim_right_if(adj_edges[1],boost ::is_any_of(","));

		vector<string> list_of_edges;
		boost::split(list_of_edges,adj_edges[1],boost::is_any_of(")"));
		
		for ( vector<string>::iterator itr = list_of_edges.begin();itr != list_of_edges.end(); itr++)
		{
			if ( *itr == "")
				continue;
			vector<string> split_v_times;
			boost ::split(split_v_times,*itr,boost::is_any_of("(")); 
			boost ::trim(split_v_times[0]);
			boost ::trim_left_if(split_v_times[0],boost::is_any_of(","));
			int vertex_v = atoi(split_v_times[0].c_str());

			/*************************************************************************
			to make sure the vertex ids are sequential
			*************************************************************************/
			map<int,int>::iterator to_find_new_id_v =  this->vertex_id_mapping.find(vertex_v);
			 if (to_find_new_id_v == this->vertex_id_mapping.end())
			 {
				 this->vertex_id_mapping.insert(pair<int,int>(vertex_v,global_vertex_id));
				 newer_vertex_v = global_vertex_id;
				 global_vertex_id++;

			 }
			 else
			 {
				 newer_vertex_v = to_find_new_id_v->second;
			 }
			
			/*****************************************************************************
			this needs a little bit of fix up.. need to find out whether the vertex combinations
			should be of the newer vertex ids or that of the old ones
			*******************************************************************************/


			/*string end_vertexes_1 = adj_edges[0] + "," + split_v_times[0];
			string end_vertexes_2 = split_v_times[0] + "," + adj_edges[0];*/
			string end_vertexes_1 = boost::lexical_cast<string>(newer_vertex_u)+","+boost::lexical_cast<string>(newer_vertex_v);
			string end_vertexes_2 = boost::lexical_cast<string>(newer_vertex_v)+","+boost::lexical_cast<string>(newer_vertex_u);

			map<string,int>::iterator edge_id_map_itr = edge_id_mapping.find(end_vertexes_1);
			if ( edge_id_map_itr == edge_id_mapping.end())
			{

				edge_id_mapping.insert(pair<string,int>(end_vertexes_1,global_edge_id));
				edge_id_mapping.insert(pair<string,int>(end_vertexes_2,global_edge_id));
			
				vector<string> timestamps;
				boost :: split(timestamps,split_v_times[1],boost::is_any_of(","));
				for ( vector<string>::iterator sub_itr = timestamps.begin();sub_itr!=timestamps.end(); sub_itr++)
				{
					double curr_timestamp = boost ::lexical_cast<double>(*sub_itr);
					map<double,vector<string>>::iterator time_itr = time_sorted_mwadj.find(curr_timestamp);
					if ( time_itr != time_sorted_mwadj.end())
					{
						time_itr->second.push_back(end_vertexes_1);
					
					}
					else
					{
						vector<string> new_edge_list;
						new_edge_list.push_back(end_vertexes_1);
						time_sorted_mwadj.insert(pair<double,vector<string>>(curr_timestamp,new_edge_list));
					}

				}
				global_edge_id++;
			}

		}

		

	}
	this->no_of_vertices = global_vertex_id;
	this->no_of_edges = global_edge_id;
	
}

template<typename myDataType> 
int MwadjProcessor::compareFunction(myDataType first_val,myDataType sec_val)
{
	return first_val>=sec_val ? 1 : -1;
}

template<typename param1Type, typename param2Type>
float MwadjProcessor::calculate_sbp(param1Type param1, param2Type param2)
{
	
	 return (float)sqrt(2*param2*(std::log((double)(2*param1))/std::log(2.0)));
}


/***********************************************************************************************
Function : ProcessSystem
Input: sysFreq --- No of activities that occured at one timeinstance
	   tickno --- the tick number corresponding to the timeinstance
	   timestamp --- the actual timestamp corresponding to the timeinstance
Output: vector<chiValType> ---- list of chi_vals depending on the number of comparisons to be done. 
Description: This function calculates the system details at a given point of time and updates the 
			system properties map accordingly. 
*************************************************************************************************/

template <typename sysFreqType,typename tickNoType,typename chiValType,typename timestampType,typename sbpType >
void MwadjProcessor::processSystem(sysFreqType sysFreq, tickNoType tickno, timestampType timestamp, vector<chiValType>& listofchis,sbpType& sbp)
{
	map<double,mySystem>::reverse_iterator prev_sys_itr = this->system_properties.rbegin();
	mySystem currSysDetails;
	currSysDetails.tick_time = tickno;
	currSysDetails.freq = prev_sys_itr->second.freq + sysFreq;
	currSysDetails.firing_rate = currSysDetails.freq/currSysDetails.tick_time;
	currSysDetails.acceleration = (currSysDetails.firing_rate - prev_sys_itr->second.firing_rate)/(currSysDetails.tick_time-prev_sys_itr->second.tick_time);
	currSysDetails.chi_val = this->compareFunction(currSysDetails.firing_rate,prev_sys_itr->second.firing_rate);
	currSysDetails.acc_chi_val = this->compareFunction(currSysDetails.acceleration,prev_sys_itr->second.acceleration);
	sbp = this->calculate_sbp(currSysDetails.freq,currSysDetails.tick_time);
	currSysDetails.sbp = sbp;
	this->system_properties.insert(pair<double,mySystem>(timestamp,currSysDetails));
	listofchis.push_back(currSysDetails.chi_val);
	listofchis.push_back(currSysDetails.acc_chi_val);
	return;

	
}

template<typename param1Type, typename param2Type>
float MwadjProcessor::calc_edge_disc_wt(param1Type param1,param2Type param2)
{
	return abs(abs(param1)-param2);
}


template<typename edgeElement,typename ticktimeType, typename timestampType,typename chiValType,typename sbpType> 
void MwadjProcessor:: processEdge(edgeElement id,ticktimeType ticktime,timestampType timestamp, vector<chiValType>& listofSysChis,sbpType sbp)
{
	map<int,myEdge>::iterator tempEdge_itr = this->edge_properties.find(id);
	if ( tempEdge_itr == this->edge_properties.end())
	{
		myEdge newEdge;
		newEdge.lastticktime = ticktime;
		newEdge.lasttimestamp = timestamp;
		newEdge.freq = 1;
		newEdge.firing_rate = newEdge.freq/newEdge.lastticktime;
		newEdge.acceleration = newEdge.firing_rate/newEdge.lastticktime;
		newEdge.chi_val = listofSysChis[0];
		newEdge.acc_chi_val = listofSysChis[1];
		newEdge.disc_weight = this->calc_edge_disc_wt(newEdge.chi_val,sbp);
		this->edge_properties.insert(pair<int,myEdge>(id,newEdge));
				

	}
	else
	{
		float prev_firing_rate = tempEdge_itr->second.firing_rate;
		float prev_acceleration = tempEdge_itr->second.acceleration;
		int prev_ticktime = tempEdge_itr->second.lastticktime;
		double prev_timestamp = tempEdge_itr->second.lasttimestamp;

		tempEdge_itr->second.lastticktime = ticktime;
		tempEdge_itr->second.lasttimestamp = timestamp;
		tempEdge_itr->second.freq++;
		tempEdge_itr->second.firing_rate = tempEdge_itr->second.freq/tempEdge_itr->second.lastticktime;
		tempEdge_itr->second.acceleration = (tempEdge_itr->second.firing_rate - prev_firing_rate)/(tempEdge_itr->second.lastticktime - prev_ticktime);

		if((( tempEdge_itr->second.firing_rate >= prev_firing_rate) && (listofSysChis[0] == 1)) || ((tempEdge_itr->second.firing_rate < prev_firing_rate) && (listofSysChis[0] == -1)))
			tempEdge_itr->second.chi_val++;
		else
			tempEdge_itr->second.chi_val--;

		if (((tempEdge_itr->second.acceleration >= prev_acceleration) && (listofSysChis[1] == 1)) || ((tempEdge_itr->second.acceleration < prev_acceleration ) && ( listofSysChis[1] == -1)))
			tempEdge_itr->second.acc_chi_val++;
		else
			tempEdge_itr->second.acc_chi_val--;

		tempEdge_itr->second.disc_weight = this->calc_edge_disc_wt(tempEdge_itr->second.chi_val,sbp);
		
	}
	return;

}


template<typename vertexElement,typename ticktimeType,typename chiValType,typename timestampType,typename sbpType> 
void MwadjProcessor::processVertex(vertexElement id,ticktimeType ticktime,timestampType timestamp,vector<chiValType>& listofchis,sbpType sbp)
{
	map<int,myVertex>::iterator tempVertex_itr = this->vertex_properties.find(id);
	if ( tempVertex_itr == this->vertex_properties.end())
	{
		myVertex newVertex;
		newVertex.lastticktime = ticktime;
		newVertex.lasttimestamp = timestamp;
		newVertex.freq = 1;
		newVertex.firing_rate = newVertex.freq/newVertex.lastticktime;
		newVertex.acceleration = newVertex.firing_rate/newVertex.lastticktime;
		newVertex.chi_val = listofchis[0];
		newVertex.acc_chi_val = listofchis[1];
		this->vertex_properties.insert(pair<int,myVertex>(id,newVertex));
	}
	else
	{
		float prev_firing_rate = tempVertex_itr->second.firing_rate;
		float prev_acceleration = tempVertex_itr->second.acceleration;
		int prev_ticktime = tempVertex_itr->second.lastticktime;
		
		tempVertex_itr->second.lastticktime  = ticktime;
		tempVertex_itr->second.lasttimestamp = timestamp;
		tempVertex_itr->second.freq++;
		tempVertex_itr->second.firing_rate = tempVertex_itr->second.freq/tempVertex_itr->second.lastticktime;
		tempVertex_itr->second.acceleration = (tempVertex_itr->second.firing_rate - prev_firing_rate)/(tempVertex_itr->second.lastticktime - prev_ticktime);
		if (((tempVertex_itr->second.firing_rate >= prev_firing_rate)&&(listofchis[0] == 1)) || ((tempVertex_itr->second.firing_rate < prev_firing_rate) && (listofchis[0] == -1)))
			tempVertex_itr->second.chi_val++;
		else
			tempVertex_itr->second.chi_val--;

		if (((tempVertex_itr->second.acceleration >= prev_acceleration) && (listofchis[1] == 1)) || ((tempVertex_itr->second.acceleration < prev_acceleration) && (listofchis[1] == -1 )))
			tempVertex_itr->second.acc_chi_val++;
		else
			tempVertex_itr->second.acc_chi_val--;
		
	}
	return;
}
void MwadjProcessor ::initialiseDisjointSets(mydisjointSet& ds)
{
	for ( map<int,int>::iterator all_vertices_itr = this->vertex_id_mapping.begin();all_vertices_itr != this->vertex_id_mapping.end();all_vertices_itr++)
	{
		ds.make_set(all_vertices_itr->second);
	}

	return;
}
template<typename vertexElement,typename disjointsetType>
void MwadjProcessor ::updateMST(vertexElement src, vertexElement tar,disjointsetType& ds)
{
	curlWrapper mycurl;
	int src_parent = ds.find_set(src);
	int tar_parent = ds.find_set(tar);
	if ( src_parent != tar_parent)
	{
		add_edge(src,tar,this->MSTGraph);
		ds.link(src_parent,tar_parent);
		mycurl.add_edge(src,tar);
	}
	else
	{
		//find a path from src to tar. replace the heaviest edge.
		map<float,string> path;
		vector<int> parent(boost::num_vertices(this->MSTGraph));
		parent[src] = src;
		//boost::breadth_first_search(this->MSTGraph,src,boost::make_bfs_visitor(std::make_pair(boost::record_predecessors(&parent[0],boost::on_tree_edge()))));
		boost::breadth_first_search(this->MSTGraph,src,boost::visitor(boost::make_bfs_visitor(boost::record_predecessors(&parent[0],boost::on_tree_edge()))));
		int temp_tar = tar;
		int temp_src;
		do {
				temp_src = parent[temp_tar];
				string comb_vertices = boost::lexical_cast<std::string>(temp_src)+","+boost::lexical_cast<std::string>(temp_tar);
				int temp_edge_id = this->edge_id_mapping.find(comb_vertices)->second;
				float temp_edge_weight = this->edge_properties.find(temp_edge_id)->second.disc_weight;
				path.insert(pair<float,string>(temp_edge_weight,comb_vertices));
				temp_tar = temp_src;
		}while(temp_src != src);

		string edge_be_rem = path.rbegin()->second;
		vector<string> end_vertices;
		boost::split(end_vertices,edge_be_rem,boost::is_any_of(","));
		int final_src = boost::lexical_cast<int>(end_vertices[0]);
		int final_tar = boost::lexical_cast<int>(end_vertices[1]);
		boost::remove_edge(final_src,final_tar,this->MSTGraph);
		boost::add_edge(src,tar,this->MSTGraph);
		mycurl.remove_edge(final_src,final_tar);
		mycurl.add_edge(src,tar);

	}
	return;
}
void MwadjProcessor ::processTimeSortedData()
{
	int currtickTime = 1;

	vector<int> rank(this->no_of_vertices);
	vector<int> parent(this->no_of_vertices);

	mydisjointSet ds(&rank[0],&parent[0]);
	initialiseDisjointSets(ds);
	
	for ( map<double,vector<string>> ::iterator time_sort_itr = time_sorted_mwadj.begin();time_sort_itr != time_sorted_mwadj.end();time_sort_itr++)
	{
		int currsysFreq = time_sort_itr->second.size();
		vector<int> listofChiVals;
		float sbp = 0.0;
		this->processSystem(currsysFreq,currtickTime,time_sort_itr->first,listofChiVals,sbp);
		
		
		for ( vector<string>::iterator edge_itr = time_sort_itr->second.begin();edge_itr != time_sort_itr->second.end();edge_itr++)
		{
			vector<string> src_tar; 
			boost::split(src_tar,*edge_itr,boost::is_any_of(","));
			int src = atoi(src_tar[0].c_str());
			int tar = atoi(src_tar[1].c_str());
			int edge_id = this->edge_id_mapping.find(*edge_itr)->second;
			this->processEdge(edge_id,currtickTime,time_sort_itr->first,listofChiVals,sbp);
			this->processVertex(src,currtickTime,time_sort_itr->first,listofChiVals,sbp);
			this->processVertex(tar,currtickTime,time_sort_itr->first,listofChiVals,sbp);
			this->updateMST(src,tar,ds);
			
			/*this->processEdge(edge_id);
			this->processVertex(src);
			this->processVertex(tar);
			this->updateMST(src,tar);*/

		}
		currtickTime++;
	}
}

void main()
{
	string tempfileName = "D:\\RA\\test.mwadj";
	MwadjProcessor myInstance;
	myInstance.processinput(tempfileName);
	myInstance.processTimeSortedData();
	
}
